package com.dbms;

import com.dbms.catalog.Catalog;
import com.dbms.common.*;
import com.dbms.common.SecurityException;
import com.dbms.engine.ExecutionEngine;
import com.dbms.execution.execution.DeleteExecutor;
import com.dbms.execution.execution.Executor;
import com.dbms.execution.execution.InsertExecutor;
import com.dbms.execution.execution.SeqScanExecutor; // 新增导入：用于支持顺序扫描执行器
import com.dbms.parser.*;
import com.dbms.parser.ast.StatementNode;
import com.dbms.parser.ast.TransactionNode;
import com.dbms.storage.BufferPoolManager;
import com.dbms.storage.DiskManager;
import com.dbms.storage.Page;
import com.dbms.storage.TablePage;
import com.dbms.transaction.LogManager;
import com.dbms.transaction.LogRecord;
import com.dbms.transaction.Transaction;
import com.dbms.transaction.TransactionManager;

import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * DBMS命令行客户端入口类
 * 功能：提供用户交互界面（命令行），支持SQL输入、登录验证、结果展示，
 * 并依赖DbmsCore处理数据库核心逻辑（解析、执行、存储等）
 */
public class DBMS {

    // DBMS核心组件引用：封装了数据库的解析、执行、存储、事务等核心功能
    private final DbmsCore dbmsCore;

    /**
     * 构造方法：初始化DBMS实例，启动单机命令行模式
     * 【核心修正】通过向DbmsCore构造函数传入null，明确指定启动单机命令行模式，
     * 而非依赖特定数据库文件路径的模式
     */
    public DBMS() {
        this.dbmsCore = new DbmsCore(null);
    }

    /**
     * 启动DBMS命令行交互模式
     * 流程：打印启动信息 → 循环读取用户输入 → 处理命令（exit/login/SQL）→ 执行并展示结果 → 关闭资源
     */
    public void start() {
        // 打印启动提示和当前登录用户信息
        System.out.println("Simple DBMS has started. You are logged in as '" + dbmsCore.getCurrentUser() + "'.");
        System.out.println("Enter SQL statements ending with a semicolon, or type 'exit' to quit.");

        // 控制台输入扫描器：用于读取用户从命令行输入的内容
        Scanner scanner = new Scanner(System.in);
        // SQL语句拼接器：支持用户多行输入SQL（直到遇到分号结束）
        StringBuilder sqlBuilder = new StringBuilder();

        // 命令行交互主循环：持续接收用户输入直到输入exit
        while (true) {
            // 生成命令行提示符：格式为「当前用户 + (事务中标记)@db> 」
            // 若处于事务中，提示符带*（如 root*@db> ），否则不带（如 root@db> ）
            String prompt = dbmsCore.getCurrentUser() + (dbmsCore.isInTransaction() ? "*@db> " : "@db> ");
            System.out.print(prompt);

            // 读取用户输入的一行内容
            String line = scanner.nextLine();

            // 处理exit命令：退出交互循环
            if (line.equalsIgnoreCase("exit")) {
                break;
            }

            // =======================================================
            // ========== 在这里添加新的 login 命令处理逻辑 ==========
            // 功能：处理用户登录请求，验证用户名密码，切换当前登录用户
            // =======================================================
            if (line.trim().toLowerCase().startsWith("login")) {
                // 按空格分割命令行（处理多空格情况），获取命令参数
                String[] parts = line.split("\\s+");

                // 检查参数数量：login命令需传入 用户名 和 密码 两个参数（格式：login <username> <password>）
                if (parts.length == 3) {
                    String username = parts[1]; // 提取用户名
                    String password = parts[2]; // 提取密码

                    // 调用DbmsCore的认证方法，验证用户名密码
                    if (dbmsCore.authenticate(username, password)) {
                        // 认证成功：提示并更新当前用户
                        System.out.println("Login successful. Current user is now '" + username + "'.");
                    } else {
                        // 认证失败：提示无效的用户名或密码
                        System.err.println("Login failed: Invalid username or password.");
                    }

                    // 清空SQL拼接器：避免login命令内容混入后续SQL
                    sqlBuilder.setLength(0);
                    continue; // 跳过后续SQL处理逻辑，直接进入下一次交互循环
                } else {
                    // 参数数量错误：提示正确用法
                    System.err.println("Usage: login <username> <password>");
                    continue;
                }
            }
            // =======================================================
            // ====================== 添加结束 =======================
            // =======================================================

            // 将当前行内容拼接到SQL字符串（加空格避免行之间连在一起）
            sqlBuilder.append(line).append(" ");

            // 检查当前行是否以分号结尾：分号表示SQL语句输入完成，执行SQL
            if (line.trim().endsWith(";")) {
                // 调用DbmsCore处理SQL语句，获取执行结果
                QueryResult result = dbmsCore.processSql(sqlBuilder.toString());
                // 格式化打印执行结果
                printResult(result);
                // 清空SQL拼接器，准备接收下一条SQL
                sqlBuilder.setLength(0);
            }
        }

        // 关闭输入扫描器，释放资源
        scanner.close();
        // 关闭DBMS核心组件，释放数据库资源（如文件句柄、缓存等）
        dbmsCore.shutdown();
    }

    /**
     * 辅助方法：格式化并打印QueryResult（SQL执行结果）到控制台
     * @param result 待打印的SQL执行结果对象，包含执行状态、数据、消息等信息
     */
    private void printResult(QueryResult result) {
        // 处理执行成功的情况
        if (result.isSuccess) {
            // 若结果包含表结构（schema）和数据行（tuples），则打印表头和数据
            if (result.schema != null && !result.tuples.isEmpty()) {
                // 打印表头（列名），列之间用制表符和|分隔
                for (Column col : result.schema.getColumns()) {
                    System.out.print(col.getColumnName() + "\t | ");
                }
                // 打印分隔线，提升可读性
                System.out.println("\n----------------------------------");

                // 打印每一行数据（调用Tuple的toString()方法格式化）
                for (Tuple tuple : result.tuples) {
                    System.out.println(tuple.toString());
                }
            }
            // 打印执行成功的消息（如“影响行数：1”“表创建成功”等）
            System.out.println(result.message);
        } else {
            // 处理执行失败的情况：打印错误信息
            System.err.println("ERROR: " + result.message);
        }
        // 打印空行，分隔不同SQL的执行结果，提升可读性
        System.out.println();
    }

    /**
     * 程序入口方法：创建DBMS实例并启动命令行交互模式
     * @param args 命令行参数（当前未使用）
     */
    public static void main(String[] args) {
        DBMS cli = new DBMS();
        cli.start();
    }
}

// 以下是可添加到 DBMS.java 的 main 方法末尾（在 dbms.start() 之前）的并发测试代码
// 功能：模拟“丢失更新”场景，验证DBMS的并发控制（锁机制）是否有效
//    public static void main(String[] args) {
//        // =================== 并发测试场景：验证“丢失更新”防护 ===================
//        // 测试目的：模拟两个线程同时修改同一条数据，验证锁机制是否防止数据更新丢失
//        System.out.println("\n--- Running Concurrency 'Lost Update' Test ---");
//
//        try {
//            // 测试准备1：定义测试用数据库文件，每次测试前删除旧文件，确保环境干净
//            String DB_FILE_NAME = "concurrency_test.db";
//            File dbFile = new File(DB_FILE_NAME);
//            if(dbFile.exists()) dbFile.delete();
//
//            // 测试准备2：创建DBMS实例，用于执行测试SQL
//            DBMS dbms = new DBMS();
//
//            // 步骤1：初始化测试数据——创建账户表并插入初始记录
//            // accounts表：id（主键，账户ID），balance（余额）
//            dbms.processSql("CREATE TABLE accounts (id INT PRIMARY KEY, balance INT);");
//            // 初始数据：账户ID=1，初始余额=1000
//            dbms.processSql("INSERT INTO accounts VALUES (1, 1000);");
//
//            // 步骤2：创建两个并发线程，模拟两个用户同时修改账户余额
//            // 线程1：将账户1的余额设置为900（基于初始余额1000减100的预期）
//            Thread thread1 = new Thread(() -> {
//                System.out.println("Thread-1 wants to set balance to 900.");
//                dbms.processSql("UPDATE accounts SET balance = 900 WHERE id = 1;");
//                System.out.println("Thread-1 has finished.");
//            }, "Thread-1");
//
//            // 线程2：将账户1的余额设置为1200（基于初始余额1000加200的预期）
//            Thread thread2 = new Thread(() -> {
//                System.out.println("Thread-2 wants to set balance to 1200.");
//                dbms.processSql("UPDATE accounts SET balance = 1200 WHERE id = 1;");
//                System.out.println("Thread-2 has finished.");
//            }, "Thread-2");
//
//            // 步骤3：同时启动两个线程，模拟并发执行
//            thread1.start();
//            thread2.start();
//
//            // 步骤4：等待两个线程完全执行完毕（避免主线程提前查询导致结果不准确）
//            thread1.join();
//            thread2.join();
//
//            // 步骤5：查询最终结果，验证是否存在“丢失更新”
//            // 若锁机制有效：最终余额应为900或1200（取决于哪个线程先获取锁），而非其他值
//            System.out.println("\n--- Concurrency Test Finished ---");
//            System.out.println("--- Final account balance: ---");
//            dbms.processSql("SELECT * FROM accounts WHERE id = 1;");
//
//            // 测试结束：关闭DBMS，释放资源
//            dbms.shutdown();
//
//        } catch(Exception e) {
//            // 捕获测试过程中的异常（如SQL错误、线程中断等），打印异常信息
//            e.printStackTrace();
//        }
//    }